package com.fvff.sanctuary.model;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CollectionOfElements;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import java.io.Serializable;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;

/**
 * Person
 * <p/>
 * This is what it is all about in Sanctuary. The world revolves around one of these.
 * Modeled after the OpenSocial JavaScript API as a starting block,
 * also to allow for future integration with open social.
 */
@Entity(name = "PERSON")
public final class Person implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "PERSON_ID")
    private Long id;

    @Column(name = "OPEN_SOCIAL_ID")
    private String openSocialId;

    @Column(name = "ABOUT_ME")
    private String aboutMe;

    @CollectionOfElements(fetch = FetchType.LAZY)
    @JoinTable(name = "PERSON_ACTIVITY",
        joinColumns = @JoinColumn(name = "PERSON_ACTIVITY_ID"))
    @Cascade(org.hibernate.annotations.CascadeType.REMOVE)
    @Column(name = "ACTIVITY")
    private Set<String> activities = new HashSet<String>();

    @OneToMany(cascade = CascadeType.REMOVE, fetch = FetchType.LAZY, mappedBy = "person")
    private Set<Address> addresses = new HashSet<Address>();

    @Column(name = "AGE")
    private Integer age;

    @Column(name = "BOOKS")
    @CollectionOfElements(fetch = FetchType.LAZY)
    private Set<String> books = new HashSet<String>();

    @ManyToOne(cascade = CascadeType.REMOVE, fetch = FetchType.LAZY)
    @JoinColumn(name = "CURRENT_LOCATION_ID")
    private Address currentLocation;

    @Column(name = "DATE_OF_BIRTH")
    private Date dateOfBirth;

    @OneToMany(cascade = CascadeType.REMOVE, fetch = FetchType.LAZY, mappedBy = "person")
    private Set<Email> emails = new HashSet<Email>();

    @Column(name = "GARDEN_ACTIVATES")
    @CollectionOfElements(fetch = FetchType.LAZY)
    private Set<String> gardenActivities = new HashSet<String>();

    @Column(name = "GENDER")
    @Enumerated(EnumType.STRING)
    private Gender gender;

    @Column(name = "INTERESTS")
    @CollectionOfElements(fetch = FetchType.LAZY)
    private Set<String> interests = new HashSet<String>();

    @OneToOne(cascade = CascadeType.REMOVE, fetch = FetchType.EAGER)
    @JoinColumn(name = "NAME_ID")
    private Name name;

    @Column(name = "NICKNAME")
    private String nickname;

    @OneToOne(cascade = CascadeType.REMOVE, fetch = FetchType.LAZY)
    @JoinColumn(name = "PHONE_ID")
    private Phone phone;

    @Column(name = "PROFILE")
    private String profile;

    @Column(name = "STATUS")
    private String status;

    @Column(name = "TAGS")
    @CollectionOfElements(fetch = FetchType.LAZY)
    private Set<String> tags = new HashSet<String>();

    @ManyToOne(cascade = CascadeType.REMOVE, fetch = FetchType.LAZY)
    @JoinColumn(name = "THUMBNAIL_URL_ID")
    private Url thumbnailUrl;

    @OneToMany(cascade = CascadeType.REMOVE, fetch = FetchType.LAZY)
    @JoinTable(name = "PERSON_URLS",
            joinColumns = @JoinColumn(name = "PERSON_ID"),
            inverseJoinColumns = @JoinColumn(name = "URL_ID"))
    private Set<Url> urls = new HashSet<Url>();

    public Long getId() {
        return id;
    }

    public void setId(final Long id) {
        this.id = id;
    }

    public String getOpenSocialId() {
        return openSocialId;
    }

    public void setOpenSocialId(final String openSocialId) {
        this.openSocialId = openSocialId;
    }

    public String getAboutMe() {
        return aboutMe;
    }

    public void setAboutMe(final String aboutMe) {
        this.aboutMe = aboutMe;
    }

    /**
     * Get the Activities associated with this Person.
     * Warning!  Uses LAZY LOADING.
     *
     * @return Set of strings.
     */
    public Set<String> getActivities() {
        //  Make a copy to a) encapsulate, b) force lazy load.
        return Collections.unmodifiableSet(new HashSet<String>(activities));
    }

    /**
     * Sets the Activities associated with the Person
     * 
     * @param activities
     */
    public void setActivities(Set<String> activities) {
        if(activities == null) {
            this.activities = new HashSet<String>();
            return;
        }
        this.activities = activities;
    }

    public Set<Address> getAddresses() {
        return addresses;
    }

    public void setAddresses(final Set<Address> addresses) {
        this.addresses = addresses;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(final Integer age) {
        this.age = age;
    }

    public Set<String> getBooks() {
        return books;
    }

    public void setBooks(final Set<String> books) {
        this.books = books;
    }

    public Address getCurrentLocation() {
        return currentLocation;
    }

    public void setCurrentLocation(final Address currentLocation) {
        this.currentLocation = currentLocation;
    }

    public Date getDateOfBirth() {
        return dateOfBirth;
    }

    public void setDateOfBirth(final Date dateOfBirth) {
        this.dateOfBirth = dateOfBirth;
    }

    public Set<Email> getEmails() {
        return emails;
    }

    public void setEmails(final Set<Email> emails) {
        this.emails = emails;
    }

    public Set<String> getGardenActivities() {
        return gardenActivities;
    }

    public void setGardenActivities(final Set<String> gardenActivities) {
        this.gardenActivities = gardenActivities;
    }

    public Gender getGender() {
        return gender;
    }

    public void setGender(final Gender gender) {
        this.gender = gender;
    }

    public Set<String> getInterests() {
        return interests;
    }

    public void setInterests(final Set<String> interests) {
        this.interests = interests;
    }

    public Name getName() {
        return name;
    }

    public void setName(final Name name) {
        this.name = name;
    }

    public String getNickname() {
        return nickname;
    }

    public void setNickname(final String nickname) {
        this.nickname = nickname;
    }

    public Phone getPhone() {
        return phone;
    }

    public void setPhone(final Phone phone) {
        this.phone = phone;
    }

    public String getProfile() {
        return profile;
    }

    public void setProfile(final String profile) {
        this.profile = profile;
    }

    public String getStatus() {
        return status;
    }

    public void setStatus(final String status) {
        this.status = status;
    }

    public Set<String> getTags() {
        return tags;
    }

    public void setTags(final Set<String> tags) {
        this.tags = tags;
    }

    public Url getThumbnailUrl() {
        return thumbnailUrl;
    }

    public void setThumbnailUrl(final Url thumbnailUrl) {
        this.thumbnailUrl = thumbnailUrl;
    }

    public Set<Url> getUrls() {
        return urls;
    }

    public void setUrls(final Set<Url> urls) {
        this.urls = urls;
    }

    @Override
    public boolean equals(final Object o) {
        return o != null && (o == this || o.getClass() == getClass() && EqualsBuilder.reflectionEquals(this, o));
    }

    @Override
    public int hashCode() {
        return HashCodeBuilder.reflectionHashCode(this);
    }

    @Override
    public String toString() {
        return new ToStringBuilder(this).
                append("id", id).
                append("openSocialId", openSocialId).
                append("name", name).
                toString();
    }
}
